package org.example.video.crontab;

import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.example.video.dao.VideoRepository;
import org.example.video.pojo.Video;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Slf4j
public class TranscodingJob {

    private final MinioClient minioClient;
    private final VideoRepository videoRepo;

    @Value("${video.bucket.hls}")
    private String hlsBucket;
    @Value("${video.prefix.hls}")
    private String hlsPrefix;

    public TranscodingJob(MinioClient minioClient, VideoRepository videoRepo) {
        this.minioClient = minioClient;
        this.videoRepo = videoRepo;
    }

    @Scheduled(fixedDelayString = "${transcode.poll.fixedDelay}")
    public void pollAndTranscode() {
        log.info("开始轮询视频转码任务");
        List<Video> list = videoRepo.findByStatus(Video.Status.PENDING);
        for (Video video : list) {
            try {
                doTranscode(video);
                videoRepo.markReady(video.getId(), hlsPrefix + "/" + video.getId() + "/playlist.m3u8");
                log.info("视频切片成功: {}", video.getId());
            } catch (Exception e) {
                log.error("视频切片失败: {}", video.getId(), e);
                videoRepo.markError(video.getId(), e.getMessage());
            }
        }
        log.info("视频转码任务轮询完成，共处理 {} 个视频", list.size());
    }

    private void doTranscode(Video video) throws Exception {
        String objectRaw = video.getObjectRaw();
        UUID id = video.getId();
        log.info("开始处理视频转码，视频 ID：{}", id);

        // 1. 从 Minio 下载原始视频到本地临时文件
        Path tempDir = Files.createTempDirectory("video-transcode-" + id);
        File localRaw = tempDir.resolve("input.mp4").toFile();
        log.info("创建临时文件：{}", localRaw.getAbsolutePath());

        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket(video.getBucketRaw())
                .object(objectRaw)
                .build();
        try (InputStream is = minioClient.getObject(getObjectArgs);
             FileOutputStream fos = new FileOutputStream(localRaw)) {
            is.transferTo(fos);
            log.info("原始视频下载完成，视频 ID：{}", id);
        }

        // 2. 创建 HLS 输出目录
        File hlsDir = tempDir.resolve("hls").toFile();
        hlsDir.mkdirs();
        String playlist = new File(hlsDir, "playlist.m3u8").getAbsolutePath();
        log.info("创建 HLS 输出目录：{}", hlsDir.getAbsolutePath());

        // 3. 使用 JavaCV 进行视频转码
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(localRaw)) {
            grabber.start();

            // 获取视频信息
            int width = grabber.getImageWidth();
            int height = grabber.getImageHeight();
            double frameRate = grabber.getVideoFrameRate();
            int audioChannels = grabber.getAudioChannels();
            int audioSampleRate = grabber.getSampleRate();

            // 配置 HLS 录制器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(playlist, width, height, audioChannels);
            recorder.setFormat("hls");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder.setFrameRate(frameRate);
            recorder.setSampleRate(audioSampleRate);
//            recorder.setVideoBitrate(2000000); // 2Mbps
//            recorder.setAudioBitrate(128000);  // 128kbps

            // 按源码率转码
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            recorder.setAudioBitrate(grabber.getAudioBitrate());
            // 使用 ffmpeg 参数设置 HLS 相关选项
            recorder.setOption("hls_time", "10");           // 每个切片的时长（秒）
            recorder.setOption("hls_playlist_type", "vod"); // VOD 模式
            recorder.setOption("hls_segment_filename", hlsDir.getAbsolutePath() + "/segment_%03d.ts"); // 切片文件名格式
            recorder.start();

            AtomicInteger processedFrames = new AtomicInteger(0);
            // 开始转码
            Frame frame;
            while ((frame = grabber.grab()) != null) {
                recorder.record(frame);
                int current = processedFrames.incrementAndGet();
                if (current % 200 == 0) { // 每100帧记录一次进度
                    log.info("视频转码进行中, 视频 ID: {}", id);
                }
            }

            // 完成转码
            recorder.stop();
            recorder.release();
            log.info("视频转码完成，视频 ID：{}", id);

            // 4. 上传 HLS 文件到 Minio
            File[] files = hlsDir.listFiles((d, name) -> name.endsWith(".ts") || name.endsWith(".m3u8"));
            if (files == null || files.length == 0) {
                throw new RuntimeException("未找到生成的 HLS 文件");
            }

            log.info("开始上传 HLS 文件到 Minio，共 {} 个文件", files.length);
            for (File file : files) {
                String key = hlsPrefix + "/" + id + "/" + file.getName();
                try (InputStream fis = new FileInputStream(file)) {
                    PutObjectArgs build = PutObjectArgs.builder()
                            .bucket(hlsBucket)
                            .object(key)
                            .stream(fis, file.length(), -1)
                            .contentType(file.getName().endsWith(".m3u8")
                                    ? "application/vnd.apple.mpegurl"
                                    : "video/MP2T")
                            .build();
                    minioClient.putObject(build);
                    log.info("文件上传成功：{}", key);
                }
            }
        } finally {
            // 5. 清理临时文件
            deleteDirectory(tempDir.toFile());
            log.info("清理临时文件完成：{}", tempDir);
        }
    }

    /*
    private void doTranscode(Video video) throws Exception {
        String objectRaw = video.getObjectRaw();
        UUID id = video.getId();
        log.info("开始处理视频转码，视频 ID：{}", id);

        // 1. 从 Minio 下载原始视频到本地临时文件
        File localRaw = Files.createTempFile(id.toString(), ".mp4").toFile();
        log.info("创建临时文件：{}", localRaw.getAbsolutePath());
        GetObjectArgs build = GetObjectArgs.builder()
                .bucket(video.getBucketRaw())
                .object(objectRaw)
                .build();
        try (InputStream is = minioClient.getObject(build); FileOutputStream fos = new FileOutputStream(localRaw)) {
            is.transferTo(fos);
            log.info("原始视频下载完成，视频 ID：{}", id);
        }

        // 2. 创建临时目录存储 HLS 切片文件
        File hlsDir = Files.createTempDirectory("hls-" + id).toFile();
        log.info("创建 HLS 临时目录：{}", hlsDir.getAbsolutePath());
        String playlist = new File(hlsDir, "playlist.m3u8").getAbsolutePath();
        String segPattern = new File(hlsDir, "segment_%03d.ts").getAbsolutePath();

        // 3. 使用 FFmpeg 命令将本地视频转码为 HLS 格式
        ProcessBuilder pb = new ProcessBuilder(
                "ffmpeg", "-i", localRaw.getAbsolutePath(),
                "-c:v", "libx264", "-c:a", "aac",
                "-hls_time", "10",
                "-hls_playlist_type", "vod",
                "-hls_segment_filename", segPattern,
                playlist
        );
        log.info("开始执行 FFmpeg 转码命令");
        pb.inheritIO();
        int code = pb.start().waitFor();
        if (code != 0) {
            log.error("FFmpeg 转码失败，视频 ID：{}", id);
            throw new RuntimeException("FFmpeg 转码失败");
        }
        log.info("FFmpeg 转码成功，视频 ID：{}", id);

        // 4. 将生成的 HLS 文件上传到 Minio
        File[] files = hlsDir.listFiles((d, name) -> name.endsWith(".ts") || name.endsWith(".m3u8"));
        if (files == null || files.length == 0) {
            log.error("未找到生成的 HLS 文件，视频 ID：{}", id);
            throw new RuntimeException("未找到生成的 HLS 文件");
        }
        log.info("开始上传 HLS 文件到 Minio，共 {} 个文件", files.length);
        for (File file : files) {
            String key = hlsPrefix + "/" + id + "/" + file.getName();
            try (InputStream fis = new FileInputStream(file)) {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(hlsBucket)
                                .object(key)
                                .stream(fis, file.length(), -1)
                                .contentType(file.getName().endsWith(".m3u8")
                                        ? "application/vnd.apple.mpegurl"
                                        : "video/MP2T")
                                .build()
                );
                log.info("文件上传成功：{}", key);
            }
        }
        log.info("所有 HLS 文件上传完成，视频 ID：{}", id);

        // 5. 删除临时文件
        log.info("开始清理临时文件");
        localRaw.delete();
        log.info("删除临时文件：{}", localRaw.getAbsolutePath());
        deleteDirectory(hlsDir);
        log.info("删除临时目录：{}", hlsDir.getAbsolutePath());
    }
    */

    // 辅助方法：删除目录及其内容
    private void deleteDirectory(File directory) {
        if (directory == null || !directory.exists()) return;
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    file.delete();
                }
            }
        }
        directory.delete();
    }
}